﻿using System;
using System.Xml;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools;

namespace BasecampSync
{
    class MilestoneSync
    {
        private BasecampApi api;
        private Outlook.MAPIFolder myFolder;

        public MilestoneSync(Outlook._Application outlookObj, BasecampApi api)
        {
            this.api = api;
            this.myFolder = (Outlook.MAPIFolder)outlookObj.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
        }

        public void Sync(List<Project> projects)
        {
            foreach (Project curProject in projects)
            {
                SyncProject(curProject);   
            }
        }

        private void SyncProject(Project project)
        {
            Outlook.MAPIFolder folder = FolderExists(project.Name);

            if (folder == null)
            {
                // Folder named like the existing basecamp project does not exists
                // thus we assume, that the milestones of that project havent been imported yet
                InsertCalendar(project);
            }
            else
            {
                CompareBasecampOutlook(project, folder);
            }
        }

        private void CompareBasecampOutlook(Project project, Outlook.MAPIFolder folder)
        {
            Stack<uint> updatedIds = new Stack<uint>();


            foreach (Outlook.AppointmentItem curAppointment in folder.Items)
            {
                string id;

                try 
                {
                    id = curAppointment.UserProperties["basecamp-id"].Value.ToString();
                } 
                catch
                {
                    continue;
                }

                if (String.IsNullOrEmpty(id))
                {
                    continue;
                }

                Milestone importedMilestone;

                try
                {
                     importedMilestone = project.Contains(Convert.ToUInt32(id));
                }
                catch (ArgumentException)
                {
                    // Milestone with that basecamp id is not found,
                    // delete the associated Outlook appointment
                    curAppointment.Delete();
                    continue;
                }

                // Update milestone in outlook
                UpdateAppointment(curAppointment, importedMilestone);
                updatedIds.Push(Convert.ToUInt32(id));
            }


            // Now, test if there are some new milestones that aren't imported and thus
            // not updated yet.
            foreach(Milestone curMilestone in project.Milestones)
            {
                if (updatedIds.Contains(curMilestone.Id))
                {
                    // Milestone was allready imported and updated
                    continue;
                }

                // Milestone has to be imported to Outlook
                Outlook.AppointmentItem appointment =
                    (Outlook.AppointmentItem)folder.Items.Add(Outlook.OlItemType.olAppointmentItem);

                UpdateAppointment(appointment, curMilestone);

                // User properties
                appointment.UserProperties.Add("basecamp-id", Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText, false, null);
                appointment.UserProperties["basecamp-id"].Value = curMilestone.Id.ToString();

                appointment.Save();
            }
        }

        /// <summary>
        /// Copies the content of a basecamp milestone to an existing outlook appointment.
        /// User properties wont be set in this function.
        /// </summary>
        /// <param name="appointment">Outlook appointment</param>
        /// <param name="milestone">Basecamp milestone</param>
        private static void UpdateAppointment(Outlook.AppointmentItem appointment, Milestone milestone)
        {
            // Set boundaries
            appointment.Start = milestone.Created;
            appointment.End = milestone.Deadline;

            // Set subject
            if (milestone.Title.Length > 15)
            {
                appointment.Subject = milestone.Title.Substring(0, 13) + "...";
            }
            else
            {
                appointment.Subject = milestone.Title;
            }

            appointment.Body = milestone.Title;
            appointment.Save();
        }

        /// <summary>
        /// Creates a new Outlook calendar that contains imported milestones.
        /// </summary>
        /// <param name="project">Basecamp project that contains the milestones, we want to import</param>
        private void InsertCalendar(Project project)
        {
            // Create Folder
            Outlook.MAPIFolder listFolder =
              this.myFolder.Folders.Add(project.Name, Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderCalendar);

            foreach (Milestone curMilestone in project.Milestones)
            {
                // Create new Outlook appointment
                Outlook.AppointmentItem appointment =
                    (Outlook.AppointmentItem)listFolder.Items.Add(Outlook.OlItemType.olAppointmentItem);

                if (curMilestone.Title.Length > 15)
                {
                    appointment.Subject = curMilestone.Title.Substring(0, 13) + "...";
                }
                else
                {
                    appointment.Subject = curMilestone.Title;
                }

                // Date boundaries
                appointment.Start = curMilestone.Created;
                appointment.End = curMilestone.Deadline;

                appointment.UserProperties.Add("basecamp-id", Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText, false, null);
                appointment.UserProperties["basecamp-id"].Value = curMilestone.Id.ToString();

                appointment.Save();
            }
        }

        public Outlook.MAPIFolder FolderExists(string needle)
        {
            foreach (Outlook.MAPIFolder subFolder in this.myFolder.Folders)
            {
                if (subFolder.Name == needle)
                {
                    return subFolder;
                }
            }

            return null;
        }
    }
}
